#include "net_client.hpp"

namespace IotClient {

TcpClient::TcpClient(const std::string& serv_ip, int port) : servip_(serv_ip), port_(port)
{
    if(serv_ip.empty() || port <= 0)
    {
        throw std::runtime_error("ERROR:Invalid server IP or poet specified");
    }
}

TcpClient::~TcpClient()
{
    closeConnection();
}

void TcpClient::closeConnection()
{
    if(fd_ >= 0)
    {
        std::cout << "INFO:Closing socket connection to [" << servip_ << ":" << port_ << "]" << std::endl;
        close(fd_);
        fd_ = -1;
    }
    is_connected_ = false;
}

void TcpClient::connectServer()
{
    //关闭旧连接，防止重复调用泄露fd
    closeConnection();

    struct addrinfo hints, *res = nullptr, *rp;
    int rv;
    bool connected = false;
    std::string port_str = std::to_string(port_);

    std::memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;

    rv = getaddrinfo(servip_.c_str(), port_str.c_str(), &hints, &res);
    if(rv != 0)
    {
        throw std::runtime_error("ERROR:getadrinfo failed: " + std::string(gai_strerror(rv)));
    }

    for(rp=res; rp!=nullptr; rp=rp->ai_next)
    {
        fd_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
        if(fd_ < 0)
        {
            std::cerr << "DEBUG: Create socket failed:" << std::strerror(errno) << std::endl;
            continue;
        }

        int optval = 1;
        if(setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0)
        {
            std::cerr << "DEBUG:setsockopt SO_REUSEADDR failed:" << std::strerror(errno) << std::endl;
            close(fd_);
            fd_ = -1;
            continue;
        }

        if(connect(fd_, rp->ai_addr, rp->ai_addrlen) == 0)
        {
            connected = true;
            break; //success
        }

        std::cerr << "DEBUG: Connect to [" << servip_ << ":" << port_ << "] failed:" << std::strerror(errno) << std::endl;
        close(fd_);
        fd_ = -1;
    }

    freeaddrinfo(res);

    if (!connected) {
        throw std::runtime_error("ERROR: All connection attempts failed for [" + servip_ + ":" + port_str + "]");
    }

    is_connected_ = true;
    std::cout << "INFO: Successfully connected to [" << servip_ << ":" << port_ << "]" << std::endl;
}

bool TcpClient::checkTcpState()
{
    struct tcp_info info;
    socklen_t len = sizeof(info);

    if(fd_ < 0)
    {
        is_connected_ = false;
        return false;
    }

    if(getsockopt(fd_, IPPROTO_TCP, TCP_INFO, &info, &len) != 0)
    {
		std::cerr << "ERROR:Can not get TCP state: " << std::strerror(errno) << std::endl;
        closeConnection();
        return false;
    }

    if(info.tcpi_state == 1)
    {
        is_connected_ = true;
        return true;
    }
    else
    {
        is_connected_ = true;
        std::cout << "INFO:The connection has been dropped(state:" << info.tcpi_state << ")." << std::endl;
        closeConnection();
        return false;
    }
}

bool TcpClient::isConnected()
{
    return checkTcpState();
}

int TcpClient::sendPacked(const std::vector<uint8_t>& data)
{
    if(fd_ < 0 || !is_connected_)
    {
        if(!isConnected())
        {
            throw std::runtime_error("ERROR:Cannot send, client is not connected.");
        }
    }

    if(data.empty())
    {
        std::cerr << "WARN:Attempted to send empty data packet." << std::endl;
        return 0;
    }

    ssize_t send_bytes = send(fd_, data.data(), data.size(), 0);

    if(send_bytes < 0)
    {
        std::string error_msg = "ERROR:Send failed:" + std::string(std::strerror(errno));
        closeConnection();
        throw std::runtime_error(error_msg);
    }

    return static_cast<int>(send_bytes);
}

}
